home *** CD-ROM | disk | FTP | other *** search
/ The Arsenal Files 4 / The Arsenal Files 4 (Arsenal Computer).ISO / casm / au116-as.exe / UNARC.CPP < prev    next >
C/C++ Source or Header  |  1994-12-13  |  17KB  |  589 lines

  1. // UNARC.CPP                                 1          1    6666
  2. // Dave Harris                                11         11   6
  3. // Compiled using Borland C++ ver 3.1       1 1        1 1   6666
  4. // 03-03-94                                  1     ..   1   6   6
  5. //                                           11111 .. 11111  666
  6. ////////////////////////////////////////////////////////////////////////
  7.  
  8. #include "au.hpp"
  9.  
  10. #define PROGRAM "UNARC"  // Name of module
  11. /*********************************************************************/
  12.  
  13. typedef struct
  14. {
  15.     int  threshold;                // If number to unarc exceeds, then warn
  16. } UNARC_INFO;
  17.  
  18. /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
  19. static void problem(AU *au, char *file_name, int ret_value)
  20. {
  21.     au_printf_error(au, "\n\nProblem Unarcing %s! ", file_name);
  22.     if (ret_value == CANT_EXECUTE)
  23.         au_printf_c(au, 15, "Could not execute the unarc program\n");
  24.     else
  25.         au_printf_c(au, 15, "Unarcer returned an errorlevel of %d.\n", ret_value);
  26.  
  27.     exit(ret_value);
  28. }
  29. /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
  30. static int unarc_one(AU *au, char *file_name, char *source_directory,
  31.                      PACKAGE *package, int level)
  32. {
  33.     char string2[CLENGTH];      /* build the dos commands in the string */
  34.     char string3[CLENGTH];
  35.     int  ret_value;
  36.     int  did_rename;
  37.  
  38.     if (package->unarc == NULL)
  39.     {
  40.         au_printf_error(au, "No unarcing method specified for %s", file_name);
  41.         press_any_key(au);
  42.         return -1;
  43.     }
  44.  
  45.     did_rename = rename_strict(au, package, source_directory, file_name);
  46.  
  47.     build_fname(string2, source_directory, file_name);
  48.  
  49.     substitute_macros(string3, package->unarc, package->overwrite,
  50.             au->unarc_paths == ON ? package->unarc_path : package->unarc_no_path,
  51.             au->answer_y == ON ? package->yes_queries : NULL,
  52.             string2);
  53.  
  54.     if (package->unarc_partials == ON)
  55.     {
  56.         if (level == 0 && au->partial[0] != '\0')
  57.             sprintf(string2, "%s %s", string3, au->partial);
  58.         else
  59.             sprintf(string2, "%s *.*", string3);
  60.     }
  61.     else
  62.         strcpy(string2, string3);
  63.  
  64.     if (!au->simulate)
  65.     {
  66.         ret_value = execute(au, string2, au->output,
  67.                             package->yes_queries == NULL ? au->y_file : NULL,
  68.                             package->memoryNeeded);
  69.  
  70.         if (did_rename)
  71.             rename_strict_back(source_directory, file_name);
  72.  
  73.         if (ret_value  > 0)
  74.             problem(au, file_name, ret_value);
  75.     }
  76.     return 0;
  77. }
  78. /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
  79. // Some of the archivers are capable of stating where the archives are to
  80. // go, so in the future this function might cut down on the copies a bit
  81.  
  82. static int unarc_self_extract(AU *au, char *file_name, char *source_directory,
  83.                               char *dest_directory)
  84. {
  85.     char   string[FLENGTH];
  86.     char   string2[FLENGTH];
  87.     char   *char_ptr;
  88.     int    ret_value;
  89.     int    equal_dirs = TRUE;
  90.  
  91.     /* Copy the file from the source directory */
  92.  
  93.     if (au->scan_self == ON)
  94.     {
  95.         build_fname(string, source_directory, file_name);
  96.         ret_value = scan_one_file(au, string);
  97.         if (ret_value == au->SC_Virus_EL || (au->SC_Virus_EL == -1 && ret_value > 0))
  98.         {
  99.             add_to_bad_list(au, source_directory, file_name, 2);
  100.             return -1;
  101.         }
  102.         else if (ret_value != au->SC_NoVirus_EL || (au->SC_NoVirus_EL == -1 && ret_value != 0))
  103.             add_to_bad_list(au, au->source_directory, file_name, 4);
  104.     }
  105.  
  106.     if (stricmp(source_directory, dest_directory) != 0)
  107.     {
  108.         build_fname(string, source_directory, file_name);
  109.         sprintf(string2, "copy %s *.* >NUL", string);
  110.         execute_raw(au, string2);
  111.         equal_dirs = FALSE;
  112.     }
  113.  
  114.     strcpy(string2, file_name);
  115.  
  116.     char_ptr=strstr(string2,".");    /* wipe out .exe or .com extension */
  117.     if (char_ptr!=NULL)
  118.         *char_ptr='\0';
  119.  
  120.     if (!au->simulate)
  121.     {
  122.         if ((ret_value = execute(au, string2, au->output, au->y_file, 0)) > 0)
  123.             problem(au, file_name, ret_value);
  124.         else if (!equal_dirs)
  125.             unlink(file_name);        /* in the dest (current) dir */
  126.     }
  127.     return 0;
  128. }
  129. /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
  130. static void add_paths_to_list(AU *au, LISTPTR *dest, LISTPTR *source)
  131. {
  132.     LIST *el;
  133.     char cur_path[FLENGTH];
  134.     char path[FLENGTH];
  135.  
  136.     getcwd(cur_path, FLENGTH);
  137.  
  138.     for (el = source->head; el != NULL; el = el->next)
  139.     {
  140.         cd(au, el->data);
  141.         getcwd(path, FLENGTH);
  142.         dest->add(path);
  143.         cd(au, cur_path);
  144.     }
  145.     return;
  146. }
  147. /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
  148. static void display_elements(AU *au, LISTPTR *listPtr)
  149. {
  150.     LIST *el;
  151.  
  152.     for (el = listPtr->head; el != NULL; el=el->next)
  153.         au_printf(au, "%s\n", el->data);
  154. }
  155. /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
  156. static int check_existence(AU *au, char *dest_directory, LISTPTR *arc_paths,
  157.                            LISTPTR *arc_files)
  158. {
  159.     LIST   *el, *el2;
  160.     char    temp[FLENGTH];
  161.     char    file_name[FLENGTH];
  162.     LISTPTR existing_files;
  163.  
  164.     for (el = arc_files->head, el2 = arc_paths->head;
  165.          el != NULL && el2 != NULL; el=el->next, el2=el2->next)
  166.     {
  167.         /* Append extern and internal path, then the file name */
  168.  
  169.         build_fname(temp, dest_directory, el2->data);
  170.         build_fname(file_name, temp, el->data);
  171.  
  172.         if (access(file_name, 0x00) == 0x00)
  173.             existing_files.add(el->data);
  174.     }
  175.     if (existing_files.head != NULL)
  176.     {
  177.         if (au->warn_existing==SKIP)
  178.         {
  179.             au_printf_c(au, 15, "Skipping %s.  File archive already exists on the disk\n", file_name);
  180.             return -1;
  181.         }
  182.         if (au->warn_existing==ON && au->answer_y!=ON)
  183.         {
  184.             au_printf_c(au, 15, "\aThe following files will be overwritten if you continue:\n");
  185.             display_elements(au, &existing_files);
  186.             au_printf(au, "\n");
  187.             if (!ask_continue())
  188.                 return -1;
  189.         }
  190.     }
  191.     return 0;
  192. }
  193. /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
  194. int unarc(AU *au, char *file_name, char *dest_directory, LISTPTR *all_paths,
  195.           int level, BYTE through_unarc)
  196. {
  197.     ARC_HANDLE arc_handle;
  198.     char       hold_dir[FLENGTH];
  199.     char       next_dir[FLENGTH];
  200.     int        ret_value;
  201.     LIST       *element, *element2;
  202.     LISTPTR    arc_files, arc_paths, paths, too_long, hidden, dangerous;
  203.  
  204.     check_for_key();
  205.  
  206.     if (!ok_to_process(au, file_name))
  207.         return FALSE;
  208.  
  209.     if (arc_handle.init(au, file_name) != SUCCESS)
  210.         return FALSE;
  211.  
  212.     if (arc_handle.type > 0)
  213.     {
  214.         char *partial;
  215.         if (au->package[arc_handle.type].unarc_partials == ON)
  216.             partial = au->partial;
  217.         else
  218.             partial = "";
  219.  
  220.         get_ns(au, partial, &arc_handle, &arc_paths, &arc_files, &paths,
  221.                &too_long, &hidden, &dangerous);
  222.         arc_handle.deinit(au);
  223.  
  224.         if (dangerous.head != NULL)
  225.         {
  226.             au_printf_error(au, "The following paths are unpredictable. AU will not process this file:\n");
  227.             display_elements(au, &dangerous);
  228.             return FALSE;
  229.         }
  230.  
  231.         if (paths.head != NULL)
  232.         {
  233.             if (au->warn_path==SKIP)
  234.             {
  235.                 au_printf_c(au, 15, "Skipping %s.  Paths exist in the archive\n", file_name);
  236.                 return FALSE;
  237.             }
  238.             if (au->warn_path==ON && au->answer_y!=ON)
  239.             {
  240.                 au_printf_c(au, 15, "\aThe following paths will be used/created:\n");
  241.                 display_elements(au, &paths);
  242.                 au_printf(au, "\n");
  243.                 if (!ask_continue())
  244.                     return FALSE;
  245.             }
  246.         }
  247.  
  248.         if (too_long.head != NULL)
  249.         {
  250.             if (au->warn_non_dos==SKIP)
  251.             {
  252.                 au_printf_c(au, 15, "Skipping %s.  Non-dos files exist in the archive\n", file_name);
  253.                 return FALSE;
  254.             }
  255.             if (au->warn_non_dos==ON && au->answer_y!=ON)
  256.             {
  257.                 au_printf_c(au, 15, "The following internal file names are too long:\n");
  258.                 display_elements(au, &too_long);
  259.                 au_printf(au, "\n");
  260.                 if (!ask_continue())
  261.                     return FALSE;
  262.             }
  263.         }
  264.         if (hidden.head != NULL)
  265.         {
  266.             if (au->warn_hidden == SKIP)
  267.             {
  268.                 au_printf_c(au, 15, "Skipping %s.  Hidden/System files exist in the archive\n", file_name);
  269.                 return FALSE;
  270.             }
  271.             if (au->warn_hidden == ON && au->answer_y!=ON)
  272.             {
  273.                 au_printf_c(au, 15, "The following internal files have hidden or system attributes:\n");
  274.                 display_elements(au, &hidden);
  275.                 au_printf(au, "\n");
  276.                 if (!ask_continue())
  277.                     return FALSE;
  278.             }
  279.         }
  280.         if (au->package[arc_handle.type].overwrite_int_check == ON &&
  281.             (au->warn_existing == ON || au->warn_existing == SKIP))
  282.         {
  283.             if (check_existence(au, dest_directory, &arc_paths, &arc_files) < 0)
  284.                 return FALSE;
  285.         }
  286.  
  287.         cd(au, dest_directory, hold_dir);
  288.  
  289.         {
  290.             char string[180];
  291.             build_fname(string, hold_dir, file_name);
  292.             if (!au->no_extra)
  293.             {
  294.                 if (!through_unarc)
  295.                     au_printf(au, "    ");
  296.                 au_printf(au, "@?6Unarcing @?1%s@?H to %s", string, dest_directory);
  297.             }
  298.             if (through_unarc)
  299.                 act_log_printf(au, "   + UNARC   : %s to %s", string, dest_directory);
  300.             if (level > 0)
  301.             {
  302.                 if (!au->no_extra)
  303.                     au_printf_c(au, 15, " (level %d)", level+1);
  304.                 act_log_printf(au, " (level %d)", level+1);
  305.             }
  306.             if (!au->no_extra)
  307.                 au_printf(au, "\n");
  308.             act_log_printf(au, "\n");
  309.         }
  310.  
  311.         if (arc_handle.is_self)
  312.             ret_value = unarc_self_extract(au, file_name, hold_dir,
  313.                                            dest_directory);
  314.         else
  315.             ret_value = unarc_one(au, file_name, hold_dir,
  316.                                   &au->package[arc_handle.type], level);
  317.  
  318.         if (ret_value < 0)
  319.             return FALSE;
  320.  
  321.         if (all_paths != NULL && au->unarc_paths == ON)
  322.             add_paths_to_list(au, all_paths, &paths);
  323.  
  324.         au->num_processed[level]++;
  325.  
  326.         if (au->recurse == ON && !au->simulate)
  327.         {
  328.             for (element = arc_files.head, element2 = arc_paths.head;
  329.                 element != NULL;
  330.                 element = element->next, element2 = element2->next)
  331.             {
  332.                 if (au->unarc_paths == ON)
  333.                     cd(au, element2->data);
  334.                 getcwd(next_dir, FLENGTH);
  335.                 unarc(au, element->data, next_dir, all_paths, level+1,
  336.                       through_unarc);
  337.                 if (au->unarc_paths == ON)
  338.                     cd(au, dest_directory);
  339.             }
  340.         }
  341.         cd(au, hold_dir);
  342.         if (au->delete_behind == ON && !au->simulate)
  343.             unlink(file_name);
  344.  
  345.         return TRUE;
  346.     }
  347.     else
  348.         arc_handle.deinit(au);
  349.  
  350.     return FALSE;
  351. }
  352. /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
  353. static int unarc_internal(AU *au, char *file_name)
  354. {
  355.     unarc(au, file_name, au->dest_directory, NULL, 0, TRUE);
  356.     return 0;
  357. }
  358. /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
  359. static void end_program(void)
  360. {
  361.     if (glob_au->y_file[0] != '\0')
  362.         unlink(glob_au->y_file);
  363.     return;
  364. }
  365. /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
  366. static void ReadCFGInfo(AU *au, CFG_HANDLE *cfg_handle)
  367. {
  368.     char string[200],
  369.          string2[200],
  370.          string3[200];
  371.     UNARC_INFO *in = (UNARC_INFO *)au->info;
  372.  
  373.     for(EVER)
  374.     {
  375.         if (cfg_handle->read_line(au, string)==EOF)
  376.             break;
  377.  
  378.         split_string(string, string2);
  379.         split_string(string, string3);
  380.  
  381.         if (string2[0] == '\0')
  382.             continue;
  383.  
  384.         strcpy(au->curOpt, string2);
  385.         au->curVal = string3;
  386.         switch (toupper(string2[1]) << 8 | toupper(string2[0]))
  387.         {
  388.             case 'BE':                                          // Begin
  389.                 return;
  390.             case 'DE':                                          // Delete_behind
  391.                 au->delete_behind = get_value(au, OFF | ON);
  392.                 break;
  393.             case 'RE':                                          // Recurse
  394.                 au->recurse = get_value(au, OFF | ON);
  395.                 break;
  396.             case 'SE':                                          // Self_Extracts
  397.                 au->self_extracts = get_value(au, OFF | ON);
  398.                 break;
  399.             case 'TH':                                          // Threshold
  400.                 in->threshold = atoi(string3);
  401.                 break;
  402.             case 'DO':                                          // Dont_process
  403.                 au->dont_touch.add(string3);
  404.                 break;
  405.             case 'PA':                                          // Paths
  406.                 au->unarc_paths = get_value(au, OFF | ON);
  407.                 break;
  408.             case 'SC':                                          // Sc Slf Extracts
  409.                 au->scan_self = get_value(au, OFF | ON);
  410.                 break;
  411.             case 'WA':
  412.                 if (stricmp(string2, "WARN_NON_DOS")==0)
  413.                     au->warn_non_dos = get_value(au, OFF | ON | SKIP);
  414.                 else if (stricmp(string2, "WARN_PATH") == 0)
  415.                     au->warn_path = get_value(au, OFF | ON | SKIP);
  416.                 else if(stricmp(string2, "WARN_HIDDEN") == 0)
  417.                     au->warn_hidden = get_value(au, OFF | ON | SKIP);
  418.                 else
  419.                     au->warn_existing = get_value(au, OFF | ON | SKIP);
  420.                 break;
  421.             default:
  422.                 cfg_handle->invalid_option(au, string2);
  423.         }
  424.     }
  425. }
  426. /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
  427. static BYTE parse_comm_line(AU *au, char option, char *cur_argv,
  428.                             PARSE_TYPE type)
  429. {
  430.     switch (type)
  431.     {
  432.     case PARSE_PARAM_OPTION:
  433.         switch (option)
  434.         {
  435.         case 'A':
  436.             strcat(au->partial, cur_argv);
  437.             strcat(au->partial, " ");
  438.             break;
  439.         case 'D':                 /* Delete behind on/off */
  440.             au->delete_behind = get_value(au, OFF | ON);
  441.             break;
  442.         case 'R':                 /* Recursive on/off */
  443.             au->recurse = get_value(au, OFF | ON);
  444.             break;
  445.         case 'X':
  446.             au->self_extracts = get_value(au, OFF | ON);
  447.             break;
  448.         case 'Y':
  449.             au->answer_y = get_value(au, OFF | ON);
  450.             break;
  451.         case 'P':
  452.             if (toupper(*cur_argv) == 'A')
  453.             {
  454.                 strcpy(au->curOpt, "-PA");
  455.                 au->curVal = cur_argv+1;
  456.                 au->unarc_paths = get_value(au, OFF | ON);
  457.             }
  458.             break;
  459.         case 'W':
  460.             if (toupper(*cur_argv) == 'P')
  461.             {
  462.                 strcpy(au->curOpt, "-WP");
  463.                 au->curVal = cur_argv+1;
  464.                 au->warn_path = get_value(au, OFF | ON | SKIP);
  465.             }
  466.             else if (toupper(*cur_argv) == 'N')
  467.             {
  468.                 strcpy(au->curOpt, "-WN");
  469.                 au->curVal = cur_argv+1;
  470.                 au->warn_non_dos = get_value(au, OFF | ON | SKIP);
  471.             }
  472.             else if (toupper(*cur_argv) == 'H')
  473.             {
  474.                 strcpy(au->curOpt, "-WH");
  475.                 au->curVal = cur_argv+1;
  476.                 au->warn_hidden = get_value(au, OFF | ON | SKIP);
  477.             }
  478.             else if (toupper(*cur_argv) == 'H')
  479.             {
  480.                 strcpy(au->curOpt, "-WE");
  481.                 au->curVal = cur_argv+1;
  482.                 au->warn_existing = get_value(au, OFF | ON | SKIP);
  483.             }
  484.             break;
  485.         case '?':
  486.             au_syntax_message(au, "Unarc");
  487.             au_printf(au,
  488.                "[@?3options@?H] [@?1[src path\\]filespec@?H] [@?Bpartials@?H] [dest path]\n\n");
  489.             au_param_heading(au);
  490.             au_printf(au,
  491.                "@?3-D@?Hon|off         Delete_behind\n"
  492.                "@?3-R@?Hon|off         Recurse\n"
  493.                "@?3-X@?Hon|off         self eXtracts\n"
  494.                "@?3-Y@?Hon|off         answer Y to all questions\n"
  495.                "@?3-A@?Hon|off         Addition parameter to pass to the unarchiver\n"
  496.                "@?3-PA@?Hon|off        unarc into PAths stored in the archive if any\n"
  497.                "@?3-WP@?Hon|off|skip   Warn if archive contains Paths\n"
  498.                "@?3-WN@?Hon|off|skip   Warn if archive contains Non-DOS file names\n"
  499.                "@?3-WH@?Hon|off|skip   Warn if archive contains Hidden or system files\n"
  500.                "@?3-WE@?Hon|off|skip   Warn if archive contains files that already Exist\n");
  501.             exit (0);
  502.         default:
  503.             au_invalid_option(au, PROGRAM, option);
  504.         }
  505.         return TRUE;
  506.     case PARSE_FILESPEC:
  507.         if (au->process_list.head == NULL)
  508.             au->process_list.add(cur_argv);
  509.         else
  510.         {
  511.             if (au->dest_directory[0]=='\0' &&
  512.                (chdir(cur_argv)!=-1 || strstr(cur_argv,":") || strstr(cur_argv,"\\")))
  513.             {
  514.                 cd(au, au->cur_directory);
  515.                 strcpy(au->dest_directory, cur_argv);
  516.             }
  517.             else
  518.             {
  519.                 strcat(au->partial, cur_argv);
  520.                 strcat(au->partial, " ");
  521.             }
  522.         }
  523.         cur_argv[0]='\0';  /* destroy this command line parameter, so
  524.                                    it is not added to the list again */
  525.         return TRUE;
  526.     }
  527.     return FALSE;
  528. }
  529. /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
  530. int main_unarc(AU *au, int argc, char *argv[])
  531. {
  532.     int   i;
  533.     UNARC_INFO *in;
  534.  
  535.     in = new UNARC_INFO;
  536.     memset(in, '\0', sizeof(UNARC_INFO));
  537.     au->info = in;
  538.     in->threshold    = -1;
  539.  
  540.     atexit(end_program);
  541.  
  542.     ReadGlobalCFGInfo(au, au->cfg_file, PROGRAM, ReadCFGInfo);
  543.     generic_parse_comm_line(au, argc, argv, parse_comm_line);
  544.  
  545. /********************************************************************/
  546. /* get all the file names and put them on the list */
  547.  
  548.     cd(au, au->source_directory);
  549.     /* Restore it so we get full path name */
  550.     getcwd(au->source_directory, FLENGTH);
  551.  
  552.     check_threshold(au, in->threshold, au->answer_y==ON);
  553.  
  554. /********************************************************************
  555.    build file full of Y's
  556.  ********************************************************************/
  557.  
  558.     if (au->answer_y == ON)
  559.     {
  560.         HANDLE handle;
  561.         build_fname(au->y_file, au->cur_directory, "y__file.$$$");
  562.         handle.create(au->y_file, S_IWRITE);
  563.         for (i=0 ; i < 100 ; i++)
  564.            handle.write_text("Y\n");
  565.         handle.close();
  566.     }
  567.  
  568.     cd(au, au->cur_directory);                 /* Dest might be empty */
  569.     cd(au, au->dest_directory);
  570.     getcwd(au->dest_directory,FLENGTH);  /* Restore it so we get full path name */
  571.     cd(au, au->source_directory);
  572.  
  573.     process_files(au, unarc_internal);
  574.  
  575.     if (!au->no_extra)
  576.     {
  577.         au_printf_c(au, 15, "\nFiles Unarced = %d\n", au->num_processed[0]);
  578.  
  579.         for (i=1; i<10; i++)
  580.         {
  581.             if (au->num_processed[i]==0)
  582.                 break;
  583.             au_printf_c(au, 15, "Files Unarced (Level %d) = %d\n", i+1, au->num_processed[i]);
  584.         }
  585.     }
  586.     return 0;
  587. }
  588.  
  589.